﻿using System;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using Catel.Articles.Base.Data;
using Catel.Articles.Base.Data.Attributes;
using Catel.Articles.Base.Properties;
using Catel.Data;
using Catel.MVVM;
using Catel.MVVM.Services;

namespace Catel.Articles.Base.UI.ViewModels
{
    using Logging;

    /// <summary>
    /// Examples view model.
    /// </summary>
    public class ExamplesViewModel : ViewModelBase
	{
		#region Variables
    	private readonly string _title;
		#endregion

		#region Constructor & destructor
		/// <summary>
		/// Initializes a new instance of the <see cref="ExamplesViewModel"/> class.
		/// </summary>
		/// <param name="title">The title.</param>
		public ExamplesViewModel(string title)
		{
			_title = title;

            Log.Info("Welcome to the examples app");
            Log.StartStopwatchTrace("Searching for all examples in the current AppDomain");

            // Get all the types of the app domain
            foreach (Assembly assembly in AssemblyHelper.GetLoadedAssemblies())
            {
                try
                {
                    foreach (Type type in assembly.GetTypes())
                    {
                        if (type.IsClass)
                        {
                            ExampleAttribute[] exampleAttributes = (ExampleAttribute[])type.GetCustomAttributes(typeof(ExampleAttribute), false);
                            if (exampleAttributes.Length > 0)
                            {
                                ExampleInfo exampleInfo = new ExampleInfo(exampleAttributes[0]);
                                if (!string.IsNullOrEmpty(exampleAttributes[0].ClickHandlerName))
                                {
                                    MethodInfo methodInfo = type.GetMethod(exampleAttributes[0].ClickHandlerName,
                                        BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                                    if (methodInfo != null)
                                    {
                                        exampleInfo.ClickHandler = Delegate.CreateDelegate(typeof(VoidDelegate), methodInfo) as VoidDelegate;
                                    }
                                }

                                if (!Examples.Contains(exampleInfo))
                                {
                                    Examples.Add(exampleInfo);
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Failed to get types of an assembly");
                }
            }

            Log.StopStopwatchTrace("Searching for all examples in the current AppDomain");
            Log.Info("Found '{0}' example(s)", Examples.Count);

            if (Examples.Count > 0)
            {
                SelectedExample = Examples[0];
            }

            ThemeCollection = new Collection<ThemeInfo>();
            ThemeCollection.Add(new ThemeInfo("Aero - normal", "/Catel.Windows;component/themes/aero/catel.aero.normal.xaml"));
            ThemeCollection.Add(new ThemeInfo("Aero - large", "/Catel.Windows;component/themes/aero/catel.aero.large.xaml"));
            ThemeCollection.Add(new ThemeInfo("Expression Dark - normal", "/Catel.Windows;component/themes/expressiondark/catel.expressiondark.normal.xaml"));
            ThemeCollection.Add(new ThemeInfo("Expression Dark - large", "/Catel.Windows;component/themes/expressiondark/catel.expressiondark.large.xaml"));
            ThemeCollection.Add(new ThemeInfo("Sunny Orange", "/Catel.Windows;component/themes/sunnyorange/generic.xaml"));
            SelectedTheme = ThemeCollection[0];
		}
		#endregion

		#region Properties
    	/// <summary>
    	/// Gets the title of the view model.
    	/// </summary>
    	/// <value>The title.</value>
    	public override string Title { get { return _title; } }

        #region View model
        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get { return GetValue<string>(DescriptionProperty); }
            set { SetValue(DescriptionProperty, value); }
        }

        /// <summary>
        /// Register the Description property so it is known in the class.
        /// </summary>
        public static readonly PropertyData DescriptionProperty = RegisterProperty("Description", typeof(string), Resources.ExamplesDescription);

        /// <summary>
        /// Gets or sets a collection of themes.
        /// </summary>
        public Collection<ThemeInfo> ThemeCollection
        {
            get { return GetValue<Collection<ThemeInfo>>(ThemeCollectionProperty); }
            set { SetValue(ThemeCollectionProperty, value); }
        }

        /// <summary>
        /// Register the ThemeCollection property so it is known in the class.
        /// </summary>
        public static readonly PropertyData ThemeCollectionProperty = RegisterProperty("ThemeCollection", typeof(Collection<ThemeInfo>));

        /// <summary>
        /// Gets or sets the selected theme.
        /// </summary>
        public ThemeInfo SelectedTheme
        {
            get { return GetValue<ThemeInfo>(SelectedThemeProperty); }
            set { SetValue(SelectedThemeProperty, value); }
        }

        /// <summary>
        /// Register the SelectedTheme property so it is known in the class.
        /// </summary>
        public static readonly PropertyData SelectedThemeProperty = RegisterProperty("SelectedTheme", typeof(ThemeInfo), null,
            (sender, e) => ((ExamplesViewModel)sender).OnSelectedThemeChanged());

        /// <summary>
        /// Gets or sets the examples collection.
        /// </summary>
        public ObservableCollection<ExampleInfo> Examples
        {
            get { return GetValue<ObservableCollection<ExampleInfo>>(ExamplesProperty); }
            set { SetValue(ExamplesProperty, value); }
        }

        /// <summary>
        /// Register the Examples property so it is known in the class.
        /// </summary>
        public static readonly PropertyData ExamplesProperty = RegisterProperty("Examples", typeof(ObservableCollection<ExampleInfo>), new ObservableCollection<ExampleInfo>());

        /// <summary>
        /// Gets or sets the selected example.
        /// </summary>
        public ExampleInfo SelectedExample
        {
            get { return GetValue<ExampleInfo>(SelectedExampleProperty); }
            set { SetValue(SelectedExampleProperty, value); }
        }

        /// <summary>
        /// Register the SelectedExample property so it is known in the class.
        /// </summary>
        public static readonly PropertyData SelectedExampleProperty = RegisterProperty("SelectedExample", typeof(ExampleInfo));
        #endregion
        #endregion

        #region Methods
        /// <summary>
        /// Called when the <see cref="SelectedTheme"/> property has changed.
        /// </summary>
        private void OnSelectedThemeChanged()
        {
            var currentApp = Application.Current;
            if (currentApp == null)
            {
                var messageService = GetService<IMessageService>();
                messageService.ShowError("There is no current application object, cannot switch theme");

                return;
            }

            // Need to call this twice because the first update fixes the dictionaries, and the second one actually updates the UI
            UpdateApplicationResources(currentApp);
            UpdateApplicationResources(currentApp);
        }

        /// <summary>
        /// Updates the application resources.
        /// </summary>
        /// <param name="currentApp">The current application.</param>
        private void UpdateApplicationResources(Application currentApp)
        {
            currentApp.Resources.Clear();
            currentApp.Resources.MergedDictionaries.Clear();

            ResourceDictionary resourceDictionary = new ResourceDictionary(); 
            resourceDictionary.Source = new Uri(SelectedTheme.Source, UriKind.RelativeOrAbsolute);

            currentApp.Resources.MergedDictionaries.Add(resourceDictionary);

            Catel.Windows.StyleHelper.CreateStyleForwardersForDefaultStyles();
        }
        #endregion
    }
}
